/**************************************************************************************************
 * 
 * FILE NAME:
 *      DataGridViewMultiColumnComboColumn.cs
 * 
 * AUTHOR:
 *      Issahar Gourfinkel, 
 *      gurfi@barak-online.net
 *      This code is Completely free. I will be happy if it will help somebody.     
 * 
 * DESCRIPTION:
 *      MultiColumnCombobox simulation for DataGridView cells controls.
 * 
 * 
 * 
 * DISCLAIMER OF WARRANTY:
 *      All of the code, information, instructions, and recommendations in this software component are 
 *      offered on a strictly "as is" basis. This material is offered as a free public resource, 
 *      without any warranty, expressed or implied.
 */

using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Diagnostics;
using System.Windows.Forms;
using System.Reflection;

namespace MPMovieCatalog.Forms.Grid
{
    public class DataGridViewMultiColumnComboColumn : DataGridViewComboBoxColumn
    {
    	private List<Column> m_columns = new List<Column>();
    	
    	public List<Column> Columns
    	{
    		get
    		{
    			return m_columns;
    		}
    	}
    	
        public DataGridViewMultiColumnComboColumn()
        {
            //Set the type used in the DataGridView
            this.CellTemplate = new DataGridViewMultiColumnComboCell();
        }
        
        public class Column
        {
        	public string DisplayMember { get; set; }
        	public int Width { get; set; }
        	public int MaxWidth { get; set; }
        	
        	public Column()
        		: this(string.Empty)
        	{
        	}
        	
        	public Column(string displayMember)
        		: this(displayMember, 1)
        	{
        	}
        	
        	public Column(string displayMember, int width)
        		: this(displayMember, width, int.MaxValue)
        	{
        	}
        	
        	public Column(string displayMember, int width, int maxWidth)
        	{
        		DisplayMember = displayMember;
        		Width = width;
        		MaxWidth = maxWidth;
        	}
        	
        }
    }
    
    public class DataGridViewMultiColumnComboCell : DataGridViewComboBoxCell
    {
    	private List<DataGridViewMultiColumnComboColumn.Column> m_columns = new List<DataGridViewMultiColumnComboColumn.Column>();
    	
    	public List<DataGridViewMultiColumnComboColumn.Column> Columns
    	{
    		get
    		{
    			return m_columns;
    		}
    	}
    	
        public override Type EditType
        {
            get
            {
                return typeof(DataGridViewMultiColumnComboEditingControl);
            }
        }
        
        public override void InitializeEditingControl(int rowIndex, object initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
        {
            base.InitializeEditingControl(rowIndex, initialFormattedValue, dataGridViewCellStyle);
            DataGridViewMultiColumnComboEditingControl ctrl = DataGridView.EditingControl as DataGridViewMultiColumnComboEditingControl;
            ctrl.ownerCell = this;
        }
    }
    
    public class DataGridViewMultiColumnComboEditingControl : DataGridViewComboBoxEditingControl
    {
        private int borderWidth = 1; //TODO: make border width configurable
        private Color borderColor = Color.Gray; //TODO: make the border color configurable
        
        public DataGridViewMultiColumnComboCell ownerCell = null;
        
        public DataGridViewMultiColumnComboEditingControl()
            : base()
        {
            this.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;
            this.DropDownStyle = ComboBoxStyle.DropDownList;
        }
        
        protected override void OnDropDown(System.EventArgs e)
		{
        	if (Items != null && Items.Count > 0)
        	{
	        	List<DataGridViewMultiColumnComboColumn.Column> columns = GetColumns();
	        	
	        	int dropDownWidth = 0;
	        	foreach (DataGridViewMultiColumnComboColumn.Column column in columns)
	        	{
	        		dropDownWidth += GetColumnWidth(column);
	        	}
	        	
	        	((DataGridViewMultiColumnComboColumn)ownerCell.OwningColumn).DropDownWidth = dropDownWidth > 0 ? dropDownWidth + (columns.Count - 1) * borderWidth : 1;
        	}
			base.OnDropDown(e);						
		}

        protected override void OnDrawItem(System.Windows.Forms.DrawItemEventArgs e)
        {
            Rectangle rec = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width, e.Bounds.Height);
            
            //If there is an item
            if (e.Index > -1)
            {
                object currentObject = Items[e.Index];
                if(currentObject != null)
                {
		            SolidBrush normalBack = new SolidBrush(Color.White); //TODO: fix to be system edit box background
		            SolidBrush normalText = new SolidBrush(System.Drawing.SystemColors.ControlText);
		            
                   	SolidBrush hightlightedBack = new SolidBrush(System.Drawing.SystemColors.Highlight);
                   	SolidBrush hightlightedText = new SolidBrush(System.Drawing.SystemColors.HighlightText);
                   	
                    SolidBrush borderBrush = new SolidBrush(borderColor);
                    
		   			e.Graphics.FillRectangle(normalBack, rec);
		   			
                    if (DroppedDown && !(Margin.Top == rec.Top))
                    {
                        int currentOffset = rec.Left;
                        bool addBorder = false;
                        
                        foreach (DataGridViewMultiColumnComboColumn.Column column in GetColumns())
                        {
                        	string value = GetColumnValue(column, currentObject);
                            if(addBorder)
                            {
                                e.Graphics.DrawLine(new Pen(borderBrush, borderWidth), rec.Left + currentOffset, rec.Top, rec.Left + currentOffset, rec.Bottom);
                                currentOffset += borderWidth;
                            }
                            else
                                addBorder = true;

                            int columnWidth = GetColumnWidth(column);
                            Rectangle textRec = new Rectangle(currentOffset, rec.Y, columnWidth, rec.Height);
              
                            //now draw the relevant to this column value text
                            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
                            {
                                e.Graphics.FillRectangle(hightlightedBack, textRec);
                                e.Graphics.DrawString(value, e.Font, hightlightedText, textRec);
                            }
                            else
                            {
                                e.Graphics.FillRectangle(normalBack, textRec);
                                e.Graphics.DrawString(value, e.Font, normalText, textRec);
                            }
                            
                            currentOffset += columnWidth;
                        }
                    }
                    else
                    {
                        //if happens when the combo is closed, draw single standard item view
                        e.Graphics.DrawString(GetItemText(Items[e.Index]), e.Font, normalText, rec);
                    }
            
                }
            }
        }
        
        private List<DataGridViewMultiColumnComboColumn.Column> GetColumns()
        {
        	List<DataGridViewMultiColumnComboColumn.Column> columns = ownerCell.Columns;
        	if (columns == null || columns.Count == 0)
        	{
        		columns = ((DataGridViewMultiColumnComboColumn)ownerCell.OwningColumn).Columns;
        	}
        	
        	return columns;
        }
        
        private string GetColumnValue(DataGridViewMultiColumnComboColumn.Column column, Object obj)
        {
        	if (obj != null)
        	{
        		object valueObj = obj;
	        	if (!string.IsNullOrEmpty(column.DisplayMember))
	        	{
		        	Type objectType = obj.GetType();
		        	
		        	BindingFlags bindingflags = BindingFlags.GetProperty;
		        	MemberInfo[] members = objectType.GetMember(column.DisplayMember);
		        	if (members != null && members.Length > 0)
		        	{
		        		switch (members[0].MemberType)
		        		{
		        			case MemberTypes.Property : 
		        				bindingflags = BindingFlags.GetProperty; 
		        				break;
		        			case MemberTypes.Field : 
		        				bindingflags = BindingFlags.GetField; 
		        				break;
		        			case MemberTypes.Method : 
		        				bindingflags = BindingFlags.InvokeMethod; 
		        				break;
		        		}
		        	}
		        		
		        	valueObj = objectType.InvokeMember(column.DisplayMember, bindingflags, null, obj, null);
	        	}
	        	return valueObj.ToString();
        	}
        	return string.Empty;
        }

        private int GetColumnWidth(DataGridViewMultiColumnComboColumn.Column column)
        {
       		int columnWidth = column.Width;
	        if (columnWidth == 1)
	        {
	        	Graphics graphics = this.CreateGraphics();
	        	try
	        	{
		    		foreach (Object obj in Items)
		        	{
		    			Size size = graphics.MeasureString(GetColumnValue(column, obj), this.Font).ToSize();
	        			columnWidth = Math.Max(columnWidth, size.Width + 1);
		        	}
	        	} finally {
	        		graphics.Dispose();
	        	}
	        }
	        
	        return Math.Min(columnWidth, column.MaxWidth);
        }
    }
}